package chen.web.controller;

import java.util.HashSet;
import java.util.Set;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import chen.web.config.AbstractTableConfig;
import chen.web.config.AccountTableConfig;
import chen.web.config.ConfigRepository;
import chen.web.config.IpTableConfig;
import chen.web.config.UuidTableConfig;
import chen.web.controller.forum.JSONResponse;
import chen.web.firewall.AbstractTable;
import chen.web.firewall.AbstractTable.Type;
import chen.web.firewall.AccountTable;
import chen.web.firewall.IpSegment;
import chen.web.firewall.IpTable;
import chen.web.firewall.UuidTable;
import chen.web.user.Account;
import chen.web.user.AccountManager;

@Controller
public class FireWallController {
	
	private @Autowired IpTable ipTable;
	private @Autowired UuidTable uuidTable;
	private @Autowired AccountTable accountTable;
	
	private @Autowired AccountManager am;
	
	@RequestMapping(value="/config/firewall", method=RequestMethod.PUT)
	public @ResponseBody JSONResponse saveConfig(@RequestParam String[] allowRules, @RequestParam String[] denyRules, @RequestParam int type){
		AbstractTableConfig<?,?> config = null;
		if(type == 1){
			config = setRules(ipTable, allowRules, denyRules, IpSegment.class, ipTableCallBack);
		} else if(type == 2){
			config = setRules(uuidTable, allowRules, denyRules, String.class, uuidTableCallBack);
		} else if(type == 3){
			config = setRules(accountTable, allowRules, denyRules, Account.class, accountTableCallBack);
		} else {return JSONResponse.errorMessage("参数错误");}
		
 		ConfigRepository.instance().put(config);
 		return JSONResponse.successed(config);
	}
	
	private <T> Set<T> copy(Set<T> set){
		Set<T> newSet = new HashSet<T>();
		for(T t : set){
			newSet.add(t);
		}
		return newSet;
	}
	
	@RequestMapping(value="/config/firewall", method=RequestMethod.GET)
	public @ResponseBody JSONResponse getConfig(int type){
		AbstractTableConfig<?,?> config = null;
		if(type == 1){
			config = ConfigRepository.instance().get(IpTableConfig.class);
		} else if(type == 2){
			config = ConfigRepository.instance().get(UuidTableConfig.class);
		} else if(type == 3){
			config = ConfigRepository.instance().get(AccountTableConfig.class);
		}
		return JSONResponse.successed(config);
	}
	
	private <T> AbstractTableConfig<?,?> setRules(AbstractTable<T,?> table, String[] allowRules, String[] denyRules, Class<T> cls, CallBack callBack){
		Set<T> allow = copy(table.rules(Type.Allow));
		table.removeAll(Type.Allow);
		Set<T> deny = copy(table.rules(Type.Deny));
		table.removeAll(Type.Deny);
	
		AbstractTableConfig<?,?> config = null;
		try{
			addRules(table, allowRules, denyRules, cls);
			config = callBack.callBack(table);	
		} catch(Exception ex){
			table.removeAll(Type.Allow);
			table.removeAll(Type.Deny);
			for(T t : allow) table.addRule(t, Type.Allow);
			for(T t : deny)	 table.addRule(t, Type.Deny);
			throw new RuntimeException(ex);
		}
		return config;
	}
	
	private <T> void addRules(AbstractTable<T,?> table, String[] allowRules, String[] denyRules, Class<T> cls){
		addRules(table, allowRules, Type.Allow, cls);
		addRules(table, denyRules, Type.Deny, cls);
	}
	
	private <T> void addRules(AbstractTable<T,?> table, String[] rules, Type type, Class<T> cls){
		for(String rule : rules){
			rule = rule.trim();
			if(rule.length() == 0) continue;
			if(cls.equals(String.class)){
				table.addRule(cls.cast(rule), type);
			} else if(cls.equals(IpSegment.class)){
				table.addRule(cls.cast(IpSegment.parse(rule)), type);
			} else if(cls.equals(Account.class)){
				Account acc = am.getAccount(Integer.parseInt(rule));
				table.addRule(cls.cast(acc), type);
			} else throw new IllegalArgumentException();
		}
	}
	
	
	
	private CallBack ipTableCallBack = new CallBack(){
		@Override
		public AbstractTableConfig<?,?> callBack(AbstractTable<?, ?> table) {
			// TODO Auto-generated method stub
			IpTableConfig config = new IpTableConfig(IpTable.class.cast(table));
			ConfigRepository.instance().put(config);
			return config;
		}
	};
	
	private CallBack uuidTableCallBack = new CallBack(){
		@Override
		public AbstractTableConfig<?,?> callBack(AbstractTable<?, ?> table) {
			// TODO Auto-generated method stub
			UuidTableConfig config = new UuidTableConfig(UuidTable.class.cast(table));
			ConfigRepository.instance().put(config);
			return config;
		}
	};
	
	private CallBack accountTableCallBack = new CallBack(){
		@Override
		public AbstractTableConfig<?,?> callBack(AbstractTable<?, ?> table) {
			// TODO Auto-generated method stub
			AccountTableConfig config = new AccountTableConfig(AccountTable.class.cast(table));
			ConfigRepository.instance().put(config);
			return config;
		}
	};
}

interface CallBack{
	AbstractTableConfig<?,?>  callBack(AbstractTable<?, ?> table);
}
